package com.example.spring_aop.proxy.jdk;

import com.example.spring_aop.proxy.UserService;
import com.example.spring_aop.proxy.UserServiceImpl;
import com.example.spring_aop.proxy.static_model.User;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @className: JDKProxy
 * @author: 芃
 * @description:
 * @date: 2024/12/30 14:45
 */
public class JDKProxy {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();//创建原始对象

        //创建代理对象
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("执行日志功能——》");
                Object ret =  method.invoke(userService,args);
                System.out.println("执行功能测试时间功能——》");
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println("执行时间 = " + dateFormat.format(new Date()));
                System.out.println();
                return ret;
            }
        };
        UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), //借助原始对象的类加载器
                userService.getClass().getInterfaces(), //借助原始对象实现的接口
                handler);
        //I、Proxy.newProxyInstance类说明：使用Proxy类调用其newProxyInstance方法创建代理对象。
        //II、参数详解
        //newProxyInstance方法有三个参数，分别是ClassLoader、interfaces、InvacationHandler
        //1.ClassLoader（类加载器）:
        //为什么要是用类加载器？这是我们首先要思考的问题，对于一个类，我们知道，要想创建一个类的实体对象，那么首先必须要有这个类的Class对象，那么这个Class对象从何而来？
        // 我们一般写的代码xxx.java经过编译之后生成.class文件，.class文件内部实际上就是一些字节码，
        // JVM内部会为这个.class文件分配一个classLoader（类加载器）将这个.class文件加载到JVM内存当中变成这个类的Class对象，
        // 因为是采用动态字节码技术，因此就没有xxx.java源文件，更不会有.class文件，
        // 因此，JVM内部就无法为这个字节码生成一个Class对象，所以我们就需要一个类加载器将这个动态字节码技术生成的字节码加载到JVM当中生成一个Class对象，因此就需要借一个类加载器来实现（classLoader）。
        //借用谁的类加载器？谁的都可以了，类加载器的本质工作是通过.class文件变成Class对象，并不对这个.class文件具有其他影响，因此，我们借用其他类的加载器并不影响到其余功能。
        //2.Interfaces（和原始类相同的接口）:
        //我们说创建代理对象需要三个要素，这个interfaces参数代表的是实现哪个接口。因为要实现相同的接口（第三个要素），所以就需要指明这个代理对象实现的是哪个接口。
        // 此处我们为UserServiceImpl创建代理对象，可以通过userServiceImpl.getClass（）方法获得这个类的全部信息，进而通过getInterfaces（）方法获取它所实现的接口。
        //3.InvocationHandler（额外功能 + 原始方法）:
        //这个参数所表示的含义就是额外功能和原始对象的结合。此处采用内部类的方法区实现这个接口，因为是接口，所以不能实例化，只能通过内部类的方式来创建实现这个接口的类的实例对象。
        //
        // 为什么要实现这个接口？我们说到的代理对象的三要素当中，第一要素和第二要素我们目前并没有解决，
        // 因此，就需要通过这个接口的实现类来解决这两个因素，实现InvocationHandler接口之后，重写他的invoke方法。
        // invoke方法的功能就是书写额外功能。Invoke方法同样具有三个参数，分别代表——》Proxy=代理对象（此处我们忽略不讨论）、Method=额外功能所要增加给哪个原始方法、args【】数组=方法的参数。
        //在invoke方法当中书写额外功能的代码，同时执行原始方法，问题来了，如何让原始方法执行呢？
        // 我们知道，要执行一个方法，要知道这个方法属于哪个对象、方法名、方法参数列表，此处方法名和参数列表有了，那么方法所属对象是谁？
        //显而易见是userService，调用method的invoke方法，传递参数即可。这样就可以执行额外功能+原始方法了。
        //Method.invoke（userService，args） == userService.xxx（参数列表）
        //注意：MethodInterceptor接口和此处的InvocationHandler接口的重写方法极其相似，区别就在于invoke方法的参数不同，从而导致了原始方法的运行方式不同。
        // 这两个接口相似的原因是由于Methodinterceptor是Spring为我们提供的接口，而InvocationHandler接口是JDK为我们提供的，Spring为我们对原始方法的执行进行了封装，
        // 直接调用invocation.proceed（）的方法即可让原始方法执行。
        proxy.login("admin","123456");
        proxy.register(new User("admin", "123456"));
    }
}
